home *** CD-ROM | disk | FTP | other *** search
/ Delphi Developer's Kit 1996 / Delphi Developer's Kit 1996.iso / power / wfc007.000 / src / cservice.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-22  |  15.3 KB  |  550 lines

  1. #include <wfc.h>
  2. #pragma hdrstop
  3.  
  4. /*
  5. ** Author: Samuel R. Blackburn
  6. ** CI$: 76300,326
  7. ** Internet: sammy@sed.csc.com
  8. **
  9. ** You can use it any way you like.
  10. */
  11.  
  12. #if defined( _DEBUG )
  13. #undef THIS_FILE
  14. static char BASED_CODE THIS_FILE[] = __FILE__;
  15. #endif
  16.  
  17. CRITICAL_SECTION g_ServiceCriticalSection;
  18.  
  19. CService *CService::m_Service_p = 0;
  20.  
  21. CService::CService( LPTHREAD_START_ROUTINE thread_start_routine, DWORD controls_accepted, DWORD wait_hint )
  22. {
  23.    ASSERT( thread_start_routine != NULL );
  24.  
  25.    TRACE1( "Main tid = %#lx\n", ::GetCurrentThreadId() );
  26.  
  27.    ::InitializeCriticalSection( &g_ServiceCriticalSection );
  28.  
  29.    m_ThreadStartRoutine  = thread_start_routine;
  30.    m_ThreadHandle        = NULL;
  31.    m_ExitEventHandle     = NULL;
  32.    m_ServiceStatusHandle = 0;
  33.    m_ErrorCode           = NO_ERROR;
  34.    m_Running             = FALSE;
  35.    m_Paused              = FALSE;
  36.    m_Exiting             = FALSE;
  37.    m_Debugging           = 0;
  38.    m_ControlsAccepted    = controls_accepted;
  39.    m_WaitHint            = wait_hint;
  40.    m_CurrentState        = SERVICE_START_PENDING;
  41.    m_Service_p           = this;
  42. }
  43.  
  44. CService::~CService( void )
  45. {
  46.    ::DeleteCriticalSection( &g_ServiceCriticalSection );
  47.  
  48.    if ( m_ExitEventHandle != NULL )
  49.    {
  50.       ::CloseHandle( m_ExitEventHandle );
  51.       m_ExitEventHandle = NULL;
  52.    }
  53.  
  54.    if ( m_ThreadHandle != NULL )
  55.    {
  56.       ::CloseHandle( m_ThreadHandle );
  57.       m_ThreadHandle = NULL;
  58.    }
  59. }
  60.  
  61. BOOL CService::Initialize( LPCTSTR name_of_service )
  62. {
  63.    /*
  64.    ** Thank you Rob Williams (CI$ 73740,774) for fixing this function
  65.    */
  66.  
  67.    ASSERT( name_of_service != NULL );
  68.  
  69.    BOOL return_value = TRUE;
  70.  
  71.    // initialize m_ServiceTable
  72.  
  73.    ::strncpy( m_ServiceName, name_of_service, SERVICE_NAME_LEN );
  74.  
  75.    m_ServiceTable[ 0 ].lpServiceName = m_ServiceName;
  76.    m_ServiceTable[ 0 ].lpServiceProc = CService::ServiceMain;
  77.    m_ServiceTable[ 1 ].lpServiceName = 0;
  78.    m_ServiceTable[ 1 ].lpServiceProc = 0;
  79.  
  80.    // initiate conversation with SCM
  81.  
  82.    if ( ::StartServiceCtrlDispatcher( m_ServiceTable ) == FALSE )
  83.    {
  84.       m_ErrorCode = ::GetLastError();
  85.       return_value = FALSE;
  86.       LogEvent();
  87.    }
  88.  
  89.    return( return_value );
  90. }
  91.  
  92. void CService::AssertValid( void ) const
  93. {
  94.    ASSERT( m_Exiting             != TRUE );
  95.    ASSERT( m_ExitEventHandle     != NULL );
  96.    ASSERT( m_ServiceStatusHandle != 0    );
  97.    ASSERT( m_ThreadHandle        != NULL );
  98.    ASSERT( m_Service_p           != 0    );
  99. }
  100.  
  101. void CALLBACK CService::ServiceMain( DWORD argc, LPTSTR *argv )
  102. {
  103.    // entry point for service called by SCM when service is started
  104.  
  105.    HANDLE thread_handle = NULL;
  106.  
  107.    ASSERT( m_Service_p != NULL );
  108.  
  109.    TRACE1( "ServiceMain tid = %#lx\n", ::GetCurrentThreadId() );
  110.  
  111.    ::EnterCriticalSection( &g_ServiceCriticalSection );
  112.    m_Service_p->m_ServiceStatusHandle = ::RegisterServiceCtrlHandler( TEXT( m_Service_p->m_ServiceName ), ServiceControlManagerHandler );
  113.    ::LeaveCriticalSection( &g_ServiceCriticalSection );
  114.  
  115.    if ( m_Service_p->m_ServiceStatusHandle == (SERVICE_STATUS_HANDLE) 0 )
  116.    {
  117.       m_Service_p->m_ErrorCode = ::GetLastError();
  118.       m_Service_p->LogEvent();
  119.       m_Service_p->Exit();
  120.    }
  121.    else
  122.    {
  123.       if ( ! m_Service_p->SendStatusToServiceControlManager( SERVICE_START_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint ) )
  124.       {
  125.          goto EXIT_GOTO;
  126.       }
  127.  
  128.       ::EnterCriticalSection( &g_ServiceCriticalSection );
  129.       m_Service_p->m_ExitEventHandle = ::CreateEvent( 0, TRUE, FALSE, 0 );
  130.       ::LeaveCriticalSection( &g_ServiceCriticalSection );
  131.  
  132.       if ( m_Service_p->m_ExitEventHandle == NULL )
  133.       {
  134.          m_Service_p->m_ErrorCode = ::GetLastError();
  135.          m_Service_p->LogEvent();
  136.          m_Service_p->Exit();
  137.       }
  138.       else
  139.       {
  140.          if ( ! m_Service_p->SendStatusToServiceControlManager( SERVICE_START_PENDING, NO_ERROR, 2, m_Service_p->m_WaitHint ) )
  141.          {
  142.             goto EXIT_GOTO;
  143.          }
  144.  
  145.          m_Service_p->ParseCommandLineParameters( argc, argv );
  146.  
  147.          if ( ! m_Service_p->SendStatusToServiceControlManager( SERVICE_START_PENDING, NO_ERROR, 3, m_Service_p->m_WaitHint ) )
  148.          {
  149.             goto EXIT_GOTO;
  150.          }
  151.  
  152.          m_Service_p->OnPrepareServiceThread();
  153.  
  154.          thread_handle = ::CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)
  155.                                                m_Service_p->m_ThreadStartRoutine,
  156.                                                m_Service_p, 0,
  157.                                                &m_Service_p->m_ThreadId );
  158.  
  159.          ::EnterCriticalSection( &g_ServiceCriticalSection );
  160.          m_Service_p->m_ThreadHandle = thread_handle;
  161.          ::LeaveCriticalSection( &g_ServiceCriticalSection );
  162.  
  163.          if ( m_Service_p->m_ThreadHandle == NULL )
  164.          {
  165.             m_Service_p->m_ErrorCode = ::GetLastError();
  166.             m_Service_p->LogEvent();
  167.             m_Service_p->Exit();
  168.          }
  169.          else
  170.          {
  171.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  172.             m_Service_p->m_Running = TRUE;
  173.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  174.  
  175.             if ( m_Service_p->SendStatusToServiceControlManager( SERVICE_RUNNING ) == FALSE )
  176.             {
  177.                return;
  178.             }
  179.  
  180.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  181.             m_Service_p->m_CurrentState = SERVICE_RUNNING;
  182.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  183.  
  184.             ::WaitForSingleObject( m_Service_p->m_ExitEventHandle, INFINITE );
  185.          }
  186.  
  187. EXIT_GOTO:
  188.  
  189.          // notify SCM that service has stopped
  190.  
  191.          ASSERT( m_Service_p != 0 );
  192.  
  193.          if ( m_Service_p->m_ServiceStatusHandle != 0 )
  194.          {
  195.             m_Service_p->SendStatusToServiceControlManager( SERVICE_STOPPED, m_Service_p->m_ErrorCode );
  196.          }
  197.       }
  198.    }
  199. }
  200.  
  201. void CALLBACK CService::ServiceControlManagerHandler( DWORD control_code )
  202. {
  203.    // entry point for service called by SCM after service is started
  204.  
  205.    ASSERT( m_Service_p != 0 );
  206.  
  207.    switch( control_code )
  208.    {
  209.       case SERVICE_CONTROL_STOP:
  210.  
  211.          TRACE1( "Handling SERVICE_CONTROL_STOP tid %#lx\n", ::GetCurrentThreadId() );
  212.  
  213.          m_Service_p->SendStatusToServiceControlManager( SERVICE_STOP_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint );
  214.  
  215.          ::EnterCriticalSection( &g_ServiceCriticalSection );
  216.          m_Service_p->m_Running = FALSE;
  217.          m_Service_p->m_CurrentState = SERVICE_STOPPED;
  218.          ::LeaveCriticalSection( &g_ServiceCriticalSection );
  219.  
  220.          m_Service_p->OnStop();
  221.          m_Service_p->Exit();
  222.  
  223.          return;
  224.  
  225.       case SERVICE_CONTROL_PAUSE:
  226.  
  227.          TRACE1( "Handling SERVICE_CONTROL_PAUSE tid %#lx\n", ::GetCurrentThreadId() );
  228.  
  229.          if ( m_Service_p->m_Running == TRUE && m_Service_p->m_Paused != TRUE )
  230.          {
  231.             if ( m_Service_p->SendStatusToServiceControlManager( SERVICE_PAUSE_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint ) == FALSE )
  232.             {
  233.                return;
  234.             }
  235.  
  236.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  237.             m_Service_p->m_Paused = TRUE;
  238.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  239.  
  240.             m_Service_p->OnPause();
  241.             ::SuspendThread( m_Service_p->m_ThreadHandle );
  242.  
  243.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  244.             m_Service_p->m_CurrentState = SERVICE_PAUSED;
  245.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  246.          }
  247.  
  248.          break;
  249.  
  250.       case SERVICE_CONTROL_CONTINUE:
  251.  
  252.          TRACE1( "Handling SERVICE_CONTROL_CONTINUE tid %#lx\n", ::GetCurrentThreadId() );
  253.  
  254.          if ( m_Service_p->m_Running == TRUE && m_Service_p->m_Paused == TRUE )
  255.          {
  256.             if ( m_Service_p->SendStatusToServiceControlManager( SERVICE_CONTINUE_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint ) == FALSE )
  257.             {
  258.                return;
  259.             }
  260.  
  261.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  262.             m_Service_p->m_Paused = FALSE;
  263.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  264.  
  265.             ::ResumeThread( m_Service_p->m_ThreadHandle );
  266.             m_Service_p->OnContinue();
  267.  
  268.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  269.             m_Service_p->m_CurrentState = SERVICE_RUNNING;
  270.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  271.          }
  272.  
  273.          break;
  274.  
  275.       case SERVICE_CONTROL_INTERROGATE:
  276.  
  277.          TRACE1( "Handling SERVICE_CONTROL_INTERROGATE tid %#lx\n", ::GetCurrentThreadId() );
  278.          break;
  279.  
  280.       case SERVICE_CONTROL_SHUTDOWN:
  281.  
  282.          TRACE1( "Handling SERVICE_CONTROL_SHUTDOWN tid %#lx\n", ::GetCurrentThreadId() );
  283.          break;
  284.  
  285.       default:
  286.  
  287.          TRACE3( "Handling user-defined control code %#ld (%ld) tid %#lx\n", control_code, control_code, ::GetCurrentThreadId() );
  288.          m_Service_p->OnControlCode( control_code );
  289.  
  290.          break;
  291.    }
  292.  
  293.    m_Service_p->SendStatusToServiceControlManager( m_Service_p->m_CurrentState );
  294. }
  295.  
  296. void CService::ParseCommandLineParameters( DWORD argc , LPTSTR *argv )
  297. {
  298.    DWORD argument_number = 1;
  299.  
  300.    // default implementation
  301.    // parse command line parameters passed via SCM through ServiceMain
  302.  
  303.    while( argument_number < argc )
  304.    {
  305.       if ( argv[ argument_number ][ 0 ] == '-' || argv[ argument_number][ 0 ] == '/' )
  306.       {
  307.          switch( argv[ argument_number ][ 1 ] )
  308.          {
  309.             case 'd':
  310.             case 'D':
  311.  
  312.                ::EnterCriticalSection( &g_ServiceCriticalSection );
  313.                m_Debugging = 1;
  314.                ::LeaveCriticalSection( &g_ServiceCriticalSection );
  315.  
  316.                break;
  317.  
  318.             case 'i':
  319.             case 'I':
  320.  
  321.                char message_string[ 80 ];
  322.  
  323.                ::sprintf( message_string, "pid %#lx %ld", ::GetCurrentProcessId(), ::GetCurrentProcessId() );
  324.                ::MessageBox( NULL, message_string, m_ServiceName, MB_OK );
  325.  
  326.                break;
  327.  
  328.             default:
  329.  
  330.                break;
  331.         }
  332.       }
  333.  
  334.       argument_number++;
  335.    }
  336.  
  337. }
  338.  
  339. void CService::OnControlCode( DWORD /* dwControlCode */ )
  340. {
  341.    // default implementation
  342.    // handle user-defined control codes (128 - 255 inclusive)
  343. }
  344.  
  345. void CService::OnStop( void )
  346. {
  347.    // default implementation
  348. }
  349.  
  350. void CService::OnPrepareServiceThread( void )
  351. {
  352.    // default implementation
  353.    // allows for initialization prior to creating service thread
  354. }
  355.  
  356. void CService::OnPause( void )
  357. {
  358.    CEventLog log( m_ServiceName );
  359.    log.ReportInformation( "Service Paused" );
  360. }
  361.  
  362. void CService::OnContinue( void )
  363. {
  364.    CEventLog log( m_ServiceName );
  365.    log.ReportInformation( "Service Resumed" );
  366. }
  367.  
  368. BOOL CService::SendStatusToServiceControlManager( DWORD current_state, 
  369.                                                   DWORD win32_exit_code,
  370.                                                   DWORD check_point,
  371.                                                   DWORD wait_hint,
  372.                                                   DWORD service_specific_code )
  373. {
  374.    BOOL return_value = FALSE;
  375.  
  376.    SERVICE_STATUS service_status;
  377.  
  378.    ::ZeroMemory( &service_status, sizeof( service_status ) );
  379.  
  380.    // initialize service_status and send it to SCM
  381.  
  382.    if ( current_state == SERVICE_START_PENDING )
  383.    {
  384.       service_status.dwControlsAccepted = 0;
  385.    }
  386.    else
  387.    {
  388.       service_status.dwControlsAccepted = m_ControlsAccepted;
  389.    }
  390.  
  391.    if ( service_specific_code == 0 )
  392.    {
  393.       service_status.dwWin32ExitCode = win32_exit_code;
  394.    }
  395.    else
  396.    {
  397.       service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  398.    }
  399.  
  400.    service_status.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
  401.    service_status.dwCurrentState            = current_state;
  402.    service_status.dwServiceSpecificExitCode = service_specific_code;
  403.    service_status.dwCheckPoint              = check_point;
  404.    service_status.dwWaitHint                = wait_hint;
  405.  
  406. #if defined( _DEBUG )
  407.    DumpStatus( &service_status );
  408. #endif
  409.  
  410.    return_value = ::SetServiceStatus( m_ServiceStatusHandle, &service_status );
  411.  
  412.    if ( return_value == FALSE )
  413.    {
  414.       m_ErrorCode = ::GetLastError();
  415.       LogEvent();
  416.       Exit();
  417.    }
  418.  
  419.    return( return_value );
  420. }
  421.  
  422. void CService::Exit( void )
  423. {
  424.    ASSERT_VALID( this );
  425.  
  426.    ::EnterCriticalSection( &g_ServiceCriticalSection );
  427.  
  428.    m_Running      = FALSE;
  429.    m_CurrentState = SERVICE_STOPPED;
  430.    m_Exiting      = TRUE;
  431.  
  432.    ::LeaveCriticalSection( &g_ServiceCriticalSection );
  433.  
  434.    if ( m_ExitEventHandle != NULL )
  435.    {
  436.       ::SetEvent( m_ExitEventHandle );
  437.    }
  438. }
  439.  
  440. #pragma warning( disable : 4100 )
  441.  
  442. void CService::LogEvent( WORD event_type, LPTSTR message_string, DWORD error_code )
  443. {
  444.    CEventLog log( m_ServiceName );
  445.  
  446.    LPTSTR strings[ 1 ];
  447.  
  448.    strings[ 0 ] = message_string;
  449.  
  450.    log.Report( (CEventLog::EventType) event_type, 0, 0, 1, (const char **) strings );
  451. }
  452.  
  453. #pragma warning( default : 4100 )
  454.  
  455. #if defined( _DEBUG )
  456.  
  457. void CService::DumpStatus( SERVICE_STATUS *pStatus ) const
  458. {
  459.    TRACE( "\ncalling SetServiceStatus with:\n" );
  460.  
  461.    switch( pStatus->dwServiceType )
  462.    {
  463.       case SERVICE_WIN32_OWN_PROCESS:
  464.  
  465.          TRACE( "dwServiceType SERVICE_WIN32_OWN_PROCESS\n" );
  466.          break;
  467.  
  468.       case SERVICE_WIN32_SHARE_PROCESS:
  469.  
  470.          TRACE( "dwServiceType SERVICE_WIN32_SHARE_PROCESS\n" );
  471.          break;
  472.    }
  473.  
  474.    TRACE1( "dwControlsAccepted %#lx:\n", pStatus->dwControlsAccepted );
  475.    TRACE( "   SERVICE_CONTROL_INTERROGATE\n" );
  476.  
  477.    if ( pStatus->dwControlsAccepted & SERVICE_ACCEPT_STOP )
  478.    {
  479.       TRACE( "   SERVICE_CONTROL_STOP\n" );
  480.    }
  481.  
  482.    if ( pStatus->dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE )
  483.    {
  484.       TRACE( "   SERVICE_CONTROL_PAUSE\n" );
  485.       TRACE( "   SERVICE_CONTROL_CONTINUE\n" );
  486.    }
  487.  
  488.    if ( pStatus->dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN )
  489.    {
  490.       TRACE( "   SERVICE_CONTROL_SHUTDOWN\n" );
  491.    }
  492.  
  493.    switch( pStatus->dwCurrentState )
  494.    {
  495.       case SERVICE_STOPPED:
  496.  
  497.          TRACE( "dwCurrentState SERVICE_STOPPED\n" );
  498.          break;
  499.  
  500.       case SERVICE_START_PENDING:
  501.  
  502.          TRACE( "dwCurrentState SERVICE_START_PENDING\n" );
  503.          break;
  504.  
  505.       case SERVICE_STOP_PENDING:
  506.  
  507.          TRACE( "dwCurrentState SERVICE_STOP_PENDING\n" );
  508.          break;
  509.  
  510.       case SERVICE_RUNNING:
  511.  
  512.          TRACE( "dwCurrentState SERVICE_RUNNING\n" );
  513.          break;
  514.  
  515.       case SERVICE_CONTINUE_PENDING:
  516.  
  517.          TRACE( "dwCurrentState SERVICE_CONTINUE_PENDING\n" );
  518.          break;
  519.  
  520.       case SERVICE_PAUSE_PENDING:
  521.  
  522.          TRACE( "dwCurrentState SERVICE_PAUSE_PENDING\n" );
  523.          break;
  524.  
  525.       case SERVICE_PAUSED:
  526.  
  527.          TRACE( "dwCurrentState SERVICE_PAUSED\n" );
  528.          break;
  529.  
  530.       default:
  531.  
  532.          TRACE2( "dwCurrentState %#lx (%ld)\n", pStatus->dwCurrentState, pStatus->dwCurrentState );
  533.          break;
  534.    }
  535.  
  536.    if ( pStatus->dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR )
  537.    {
  538.       TRACE2( "dwServiceSpecificExitCode %#lx (%ld)\n", pStatus->dwServiceSpecificExitCode, pStatus->dwServiceSpecificExitCode );
  539.    }
  540.    else
  541.    {
  542.       TRACE2( "dwWin32ExitCode %#lx (%ld)\n", pStatus->dwWin32ExitCode, pStatus->dwWin32ExitCode );
  543.    }
  544.  
  545.    TRACE1( "dwCheckPoint %ld\n", pStatus->dwCheckPoint );
  546.    TRACE1( "dwWaitHint %ld\n\n", pStatus->dwWaitHint );
  547. }
  548.  
  549. #endif
  550.